﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using Zo.Events;

namespace Zo.Domain.UnitOfWorks
{
    public interface IUnitOfWork : IDisposable
    {
        /// <summary>
        /// 增加领域事件。
        /// </summary>
        /// <param name="eventItem">领域事件。</param>
        void AddEvent(IEvent eventItem);

        IUnitOfWorkResult Complete();

        Task<IUnitOfWorkResult> CompleteAsnyc();
    }

    public class UnitOfWork : IUnitOfWork
    {
        private readonly IUnitOfWorkFactory _unitOfWorkFactory;
        private readonly IEventBus _EventBus;
        private readonly TransactionScope _transactionScope;

        private List<IEvent> _Events;

        internal UnitOfWork(IUnitOfWorkFactory unitOfWorkFactory, IEventBus EventBus)
        {
            _Events = new List<IEvent>();
            _transactionScope = new TransactionScope();
            _unitOfWorkFactory = unitOfWorkFactory;
            _EventBus = EventBus;
        }

        /// <summary>
        /// 增加领域事件。
        /// </summary>
        /// <param name="eventItem">领域事件。</param>
        public void AddEvent(IEvent eventItem)
        {
            //_Events = _Events ?? new List<IEvent>();
            _Events.Add(eventItem);
        }

        /// <summary>
        /// 删除领域事件。
        /// </summary>
        /// <param name="eventItem">领域事件。</param>
        internal void RemoveEvent(IEvent eventItem)
        {
            _Events?.Remove(eventItem);
        }

        /// <summary>
        /// 清空事件。
        /// </summary>
        internal void ClearEvents()
        {
            _Events?.Clear();
        }

        /// <summary>
        /// 提交。
        /// </summary>
        public IUnitOfWorkResult Complete()
        {
            var unitOfWorkResult = new UnitOfWorkResult { Status = true };

            if (_Events.Any())
            {
                foreach (var Event in _Events)
                {
                    var EventResult = _EventBus.Publish(Event);

                    if (!EventResult.Status)
                    {
                        unitOfWorkResult.Status = false;
                        unitOfWorkResult.ErrorCode = EventResult.ErrorCode ?? string.Empty;
                        unitOfWorkResult.ErrorMessage = EventResult.ErrorMessage ?? string.Empty;
                        break;
                    }
                }
            }

            ClearEvents();

            if (unitOfWorkResult.Status)
            {
                _transactionScope.Complete();
            }

            return unitOfWorkResult;
        }

        public async Task<IUnitOfWorkResult> CompleteAsnyc()
        {
            var unitOfWorkResult = new UnitOfWorkResult { Status = true };

            if (_Events.Any())
            {
                foreach (var Event in _Events)
                {
                    await _EventBus.PublishAsync(Event);
                }
            }

            ClearEvents();

            if (unitOfWorkResult.Status)
            {
                _transactionScope.Complete();
            }

            return unitOfWorkResult;
        }

        public void Dispose()
        {
            _transactionScope.Dispose();
            _Events = null;

            _unitOfWorkFactory.Remove();
        }
    }
}